package com.dy.数组.高级.生命游戏;

/*
生命游戏，简称为生命，是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。

给定一个包含 m × n 个格子的面板，每一个格子都可以看成是一个细胞。
每个细胞具有一个初始状态 live（1）即为活细胞， 或 dead（0）即为死细胞。
每个细胞与其八个相邻位置（水平，垂直，对角线）的细胞都遵循以下四条生存定律：

如果活细胞周围八个位置的活细胞数少于两个，则该位置活细胞死亡；
如果活细胞周围八个位置有两个或三个活细胞，则该位置活细胞仍然存活；
如果活细胞周围八个位置有超过三个活细胞，则该位置活细胞死亡；
如果死细胞周围正好有三个活细胞，则该位置死细胞复活；
根据当前状态，写一个函数来计算面板上细胞的下一个（一次更新后的）状态。
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的，其中细胞的出生和死亡是同时发生的。

示例:

输入:
[
  [0,1,0],
  [0,0,1],
  [1,1,1],
  [0,0,0]
]
输出:
[
  [0,0,0],
  [1,0,1],
  [0,1,1],
  [0,1,0]
]
进阶:

你可以使用原地算法解决本题吗？请注意，面板上所有格子需要同时被更新：你不能先更新某些格子，然后使用它们的更新后的值再更新其他格子。
本题中，我们使用二维数组来表示面板。原则上，面板是无限的，但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题？
 */
public class Solution {
    public void gameOfLife(int[][] board) {
        int[][] res = new int[board.length][board[0].length];
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                res[i][j] = 0;
                int num = findLifedNum(i, j, board);
                if (num == 3) {
                    res[i][j] = 1;
                } else if (num == 2) {
                    res[i][j] = board[i][j];
                }
            }
        }
        for (int i = 0; i < board.length; i++) {
            board[i] = res[i].clone();
        }
    }

    int findLifedNum(int x, int y, int[][] board) {
        int num = 0;
        for (int i = x - 1; i <= x + 1; i++) {
            for (int j = y - 1; j <= y + 1; j++) {
                if (i < 0 || i > board.length - 1 || j < 0 || j > board[0].length - 1 || (i == x && j == y)) {
                    continue;
                }
                if (board[i][j] == 1) {
                    num++;
                }
            }
        }
        return num;
    }

    void gameOfLife2(int[][] board) {
        int m = board.length, n = board[0].length;
        /*  0 : 上一轮是0，这一轮过后还是0
            1 : 上一轮是1，这一轮过后还是1
            2 : 上一轮是1，这一轮过后变为0
            3 : 上一轮是0，这一轮过后变为1*/

        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++) {
                int lives = 0;
                //判断上边
                if (i > 0) {
                    lives += board[i - 1][j] == 1 || board[i - 1][j] == 2 ? 1 : 0;
                }
                //判断左边
                if (j > 0) {
                    lives += board[i][j - 1] == 1 || board[i][j - 1] == 2 ? 1 : 0;
                }
                //判断下边
                if (i < m - 1) {
                    lives += board[i + 1][j] == 1 || board[i + 1][j] == 2 ? 1 : 0;
                }
                //判断右边
                if (j < n - 1) {
                    lives += board[i][j + 1] == 1 || board[i][j + 1] == 2 ? 1 : 0;
                }
                //判断左上角
                if (i > 0 && j > 0) {
                    lives += board[i - 1][j - 1] == 1 || board[i - 1][j - 1] == 2 ? 1 : 0;
                }
                //判断右下角
                if (i < m - 1 && j < n - 1) {
                    lives += board[i + 1][j + 1] == 1 || board[i + 1][j + 1] == 2 ? 1 : 0;
                }
                //判断右上角
                if (i > 0 && j < n - 1) {
                    lives += board[i - 1][j + 1] == 1 || board[i - 1][j + 1] == 2 ? 1 : 0;
                }
                //判断左下角
                if (i < m - 1 && j > 0) {
                    lives += board[i + 1][j - 1] == 1 || board[i + 1][j - 1] == 2 ? 1 : 0;
                }
                //更新当前点，0，1不用更新
                if (board[i][j] == 0 && lives == 3) {
                    board[i][j] = 3;
                } else if (board[i][j] == 1) {
                    if (lives < 2 || lives > 3) board[i][j] = 2;
                }
            }
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++) {
                board[i][j] = board[i][j] % 2;
            }
    }


}
